#pragma warning(disable:4005)
#include <drogon/HttpController.h>
#include "OCMain/Environment.h"
#include "WeatherUtil.h"
#include "WeatherDB.h"
#include "maddy/parser.h"
#include "css.h"

using namespace drogon;
using namespace drogon::orm;

struct ApiDocMethodParam
{
	bool isNotNull = false;
	String name;
	String desc;
	String sample;
};

struct ApiDocMehtod
{
	String name;
	String sampleUrl;
	std::vector<ApiDocMethodParam> params;
};

const String apiTypeDesc = "anal(实况) fcst(预报)";
const String apiTypeCase = "anal1km anal3km fcst1km fcst3km";

ApiDocMehtod methodQuery = {
			"query",
			"type=anal3km&startTime=20230728000000&endTime=20230728240000",
			{
				{true, "type",apiTypeDesc,apiTypeCase},
				{true, "startTime","YYYYMMDDHHmmss","20230715000000"},
				{false, "endTime","YYYYMMDDHHmmss","20230715240000"},
			}
};

ApiDocMehtod methodFile = {
			"file",
			"type=fcst3km&g=061000&timeStamp=20230715061000&var=dbzfct",
			{
				{true, "type",apiTypeDesc,apiTypeCase},
				{true, "timeStamp","YYYYMMDDHHmmss","20230715000000"},
				{true, "var","要素","anal(Tracer,rh,temp,wwind,uwind,vwind) fcst(dbzfct,rhfct,tafct,wfct ,ufct,vfct)"},
				{false, "g","001000","HHmmss,6位,fcst类型必须]"},
			}
};

class WeatherZJService : public HttpController<WeatherZJService>
{
public:
	METHOD_LIST_BEGIN
		METHOD_ADD(WeatherZJService::home, "", Get);
	METHOD_ADD(WeatherZJService::query, "query", Get);
	METHOD_ADD(WeatherZJService::file, "file", Get);
	METHOD_LIST_END
protected:
	void home(const HttpRequestPtr& req,
		std::function<void(const HttpResponsePtr&)>&& callback)
	{
		auto resp = HttpResponse::newHttpResponse();

		StringStream mardownStream;
		mardownStream << "# **接口文档**" << "\n";

		int no = 1;
		auto pushMethod = [&mardownStream,&no](const ApiDocMehtod& methodApi) {
			mardownStream << "## " << no++ << ")   " << methodApi.name << "\n";
			mardownStream << "\n";
			mardownStream << "|table>\n";
			mardownStream << "|参数名|描述|必须|样例|\n";
			mardownStream << "- | - | -\n";
			for (auto param : methodApi.params)
			{
				mardownStream << "| " << param.name;
				mardownStream << " | " << param.desc;
				mardownStream << " | " << (param.isNotNull ? "是" : "");
				mardownStream << " |" << param.sample;
				mardownStream << "\n";
			}
			mardownStream << "|<table\n";

			mardownStream << "#### 调试\n";
			StringStream url;
			url << "WeatherZJService/" << methodApi.name << "?";
			url << methodApi.sampleUrl;

			mardownStream << "* [" << url.str() << "](" << url.str() << ")\n";
			mardownStream << "\n";
			};

		pushMethod(methodQuery);
		pushMethod(methodFile);
		
		std::shared_ptr<maddy::ParserConfig> config = std::make_shared<maddy::ParserConfig>();
		config->enabledParsers &= ~maddy::types::EMPHASIZED_PARSER; // equivalent to !isEmphasizedParserEnabled
		config->enabledParsers |= maddy::types::HTML_PARSER; // equivalent to !isHTMLWrappedInParagraph
		config->enabledParsers |= maddy::types::TABLE_PARSER; // equivalent to !isHTMLWrappedInParagraph
		std::shared_ptr<maddy::Parser> parser = std::make_shared<maddy::Parser>(config);

		StringStream bodyStream;
		bodyStream << css::x0;
		bodyStream << css::x1;
		bodyStream << css::x2;
		bodyStream << css::x3;
		bodyStream << parser->Parse(mardownStream);
		bodyStream << css::x4;

		resp->setBody(bodyStream.str());

		callback(resp);
	}

	void file(const HttpRequestPtr& req,
		std::function<void(const HttpResponsePtr&)>&& callback)
	{
		auto resp = HttpResponse::newHttpResponse();

		DbClientPtr dbClient = drogon::app().getDbClient(WeatherDB::DbName);

		if (dbClient != nullptr)
		{
			String type = req->getParameter("type");
			String var = req->getParameter("var");
			String timeStamp = req->getParameter("timeStamp");

			if (var.empty() || type.empty() || timeStamp.length() != 14)
			{
				callback(HttpResponse::newNotFoundResponse());
			}
			else
			{
				String cachePath = WeatherDB::Path;
				String date = timeStamp.substr(0, 8);
				String attachmentFileName = timeStamp + ".bin.gz";
				if (type == "anal1km" || type == "anal3km")
				{
					String fileName = joinPath(cachePath, type, date, var, attachmentFileName);
					resp = HttpResponse::newFileResponse(fileName, attachmentFileName);
				}
				else if (type == "fcst1km" || type == "fcst3km")
				{
					String g = req->getParameter("g");
					if (!g.empty())
					{
						g.insert(0, "g_");
						String fileName = joinPath(cachePath, type, date, g, var, attachmentFileName);
						resp = HttpResponse::newFileResponse(fileName, attachmentFileName);
					}
				}
			}

			callback(resp);
			return;
		}

		resp->setStatusCode(HttpStatusCode::k404NotFound);
		callback(resp);
	}

	void query(const HttpRequestPtr& req,
		std::function<void(const HttpResponsePtr&)>&& callback)
	{
		auto resp = HttpResponse::newHttpResponse();

		DbClientPtr dbClient = drogon::app().getDbClient(WeatherDB::DbName);

		if (dbClient != nullptr)
		{
			String type = req->getParameter("type");
			String startTime = req->getParameter("startTime");
			String endTime = req->getParameter("endTime");

			if (type.empty() || startTime.length() != 14)
			{
				callback(HttpResponse::newNotFoundResponse());
				return;
			}

			Json::Value resultObj;

			if (type == "anal1km")
			{
				resultObj = WeatherDB::findAnal<Anal1kmData>(startTime, endTime);
			}
			else if (type == "anal3km")
			{
				resultObj = WeatherDB::findAnal<Anal3kmData>(startTime, endTime);
			}
			else if (type == "fcst1km")
			{
				resultObj = WeatherDB::findFcst<Fcst1kmData>(startTime, endTime);
			}
			else if (type == "fcst3km")
			{
				resultObj = WeatherDB::findFcst<Fcst3kmData>(startTime, endTime);
			}

			resp->setBody(resultObj.isNull() ? "[]" : resultObj.toStyledString());
			callback(resp);
			return;
		}

		resp->setStatusCode(HttpStatusCode::k404NotFound);
		callback(resp);
	}
};